package xj.graph2d.app.project;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Comparator;
import java.util.Collections;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import xj.graph2d.Document;
import xj.graph2d.DocumentException;
import xj.lang.CodeWriter;
import xj.util.StringUtils;

import static xj.app.BaseApplicationFrame.logError;

public class ProjectFolder extends ProjectItem {

  public enum Type {
      Default, 
      UML_Model, 
      UML_Package, 
      Source_Code, 
      Java_Package,
      Documentation, 
      Test, 
      File_Folders;

    public static Type toFolderType(String name) {
      if (name != null) {
	name = name.replaceAll("_", " ");
	name = name.toLowerCase();
	if (name.indexOf("uml") >= 0) { 
	  if (name.indexOf("model") > 0) {
	    return UML_Model;
	  } else if (name.indexOf("package") > 0) {
	    return UML_Package;
	  }
	} else if (name.indexOf("java") >= 0) {
	  return Java_Package;
	} else if (name.indexOf("source") >= 0) {
	  return Source_Code;
	} else if (name.indexOf("documentation") >= 0) {
	  return Documentation;
	} else if (name.indexOf("test") >= 0) {
	  return Test;
	} else if (name.indexOf("folders") >= 0) {
	  return File_Folders;
	}
      }
      return Default;
    }
  };

  public ProjectFolder() {
  }

  public ProjectFolder(String name) {
    this(name, Type.Default);
  }

  public ProjectFolder(String name, Type ftype) {
    super(name);
    folderType = ftype;
  }

  public Type getFolderType() {
    return folderType;
  }

  public void setFolderType(Type ftype) {
    folderType = ftype;
  }

  public String getType() { 
    Type type = getFolderType();
    if (type != null) {
      return StringUtils.humanize(type.toString());
    }	
    return null;
  }

  @Override
  public boolean isChanged() {
    return changed;
  }

  public boolean isEmpty() {
    return items.isEmpty();
  }

  public void setChanged(boolean c) {
    changed = c;
  }

  public void addItem(ProjectItem item) {
    if (item != null) {
      items.add(item);
      item.setParent(this);
      changed = true;
    }
  }

  public void addItem(ProjectItem item, int pos) {
    if (item != null) {
      items.add(pos, item);
      item.setParent(this);
      changed = true;
    }
  }

  public void removeItem(ProjectItem item) {
    if (item != null) {
      items.remove(item);
      item.setParent(null);
      changed = true;
    }
  }

  public Iterator<ProjectItem> allItems() {
    return items.iterator();
  }

  public boolean hasItem(String name) {
    if (name != null) {
      Iterator<ProjectItem> iter = items.iterator();
      while (iter.hasNext()) {
	ProjectItem item = iter.next();
	if (item instanceof ProjectFolder) {
	  ProjectFolder subfolder = (ProjectFolder) item;
	  if (subfolder.hasItem(name)) {
	    return true;
	  }
	} else {
	  if (name.equals(item.getName())) {
	    return true;
	  }
	}
      }
    }
    return false;
  }

  public boolean hasChildByName(String name) {
    if (name != null) {
      Iterator<ProjectItem> iter = items.iterator();
      while (iter.hasNext()) {
	ProjectItem item = iter.next();
	if (name.equals(item.getName())) {
	  return true;
	}
      }
    }
    return false;
  }

  public ProjectItem findItem(String name) {
    return findItem(name, true);
  }

  public ProjectItem findItem(String name, boolean recurse) {
    if (name != null) {
      Iterator<ProjectItem> iter = items.iterator();
      while (iter.hasNext()) {
	ProjectItem item = iter.next();
	if (item instanceof ProjectFolder && 
	    recurse) {
	  ProjectFolder subfolder = (ProjectFolder) item;
	  ProjectItem pi = subfolder.findItem(name);
	  if (pi != null) {
	    return pi;
	  }
	} else {
	  if (name.equals(item.getName())) {
	    return item;
	  }
	}
      }
    }
    return null;
  }

  public ProjectFolder findSubFolder(String name) {
    if (name != null) {
      Iterator<ProjectItem> iter = items.iterator();
      while (iter.hasNext()) {
	ProjectItem item = iter.next();
	if (item instanceof ProjectFolder && 
	    name.equals(item.getName())) {
	  return (ProjectFolder) item;
	}
      }
    }
    return null;
  }

  public ProjectItem findItem(Document doc) {
    if (doc != null) {
      Iterator<ProjectItem> iter = items.iterator();
      while (iter.hasNext()) {
	ProjectItem item = iter.next();
	if (item instanceof ProjectFolder) {
	  ProjectFolder subfolder = (ProjectFolder) item;
	  ProjectItem pi = subfolder.findItem(doc);
	  if (pi != null) {
	    return pi;
	  }
	} else {
	  if (doc == item.getDocument()) {
	    return item;
	  }
	}
      }
    }
    return null;
  }

  public boolean hasChangedItems() {
    if (changed) {
      return true;
    }
    Iterator<ProjectItem> iter = items.iterator();
    while (iter.hasNext()) {
      ProjectItem item = iter.next();
      if (item instanceof ProjectFolder) {
	ProjectFolder subfolder = (ProjectFolder) item;
	if (subfolder.hasChangedItems()) {
	  return true;
	}
      } else {
	if (item.isChanged()) {
	  return true;
	}
      }
    }
    return false;
  }

  public void removeUnsavedNewItems() {
    Iterator<ProjectItem> iter = items.iterator();
    while (iter.hasNext()) {
      ProjectItem item = iter.next();
      if (item instanceof ProjectFolder) {
	ProjectFolder subfolder = (ProjectFolder) item;
	subfolder.removeUnsavedNewItems();
	if (subfolder.isEmpty()) { 
	  //System.out.println("ProjectFoeler.removeUnsavedNewItems() remove " + subfolder.getName());
	  iter.remove();
	}
      } else {
	if (item.isDocumentNew() && 
	    !item.isDocumentSaved()) { 
	  //System.out.println("ProjectFoeler.removeUnsavedNewItems() remove " + item.getName());
	  iter.remove();
	}
      }
    }
  }

  static NameComparator nameComp = new NameComparator();
 
  public void sortItemsByName() { 
    sortItems(nameComp);
  }

  public void sortItems(Comparator<ProjectItem> comp) { 
    if (comp != null) { 
      Collections.sort(items, comp);
    }
  }

  public static class NameComparator implements Comparator<ProjectItem> { 
    public int compare(ProjectItem item1, ProjectItem item2) { 
      if (item1 != null & item2 != null) { 
	String name1 = item1.getName();
	String name2 = item2.getName();
	if (name1 != null && name2 != null) { 
	  return name1.compareTo(name2); 
	}
      }
      return 0;
    }
  }

  @Override
  public void writeXML(CodeWriter out, String namespacePrefix) {
    if (out != null) {
      String fdname = Project.getPrefixedName(Project.TAG_NAME_PROJECT_FOLDER,
					      namespacePrefix);
      out.println("<" + fdname + " name=\"" + name + "\"" + " type=\"" + getFolderType() + "\">");
      out.thisIndentInc();
      writeItemInfo(out, namespacePrefix);
      writeItemsXML(out, namespacePrefix);
      out.thisIndentDec();
      out.println("</" + fdname + ">");
    }
  }

  public void writeItemsXML(CodeWriter out, String namespacePrefix) {
    if (out != null) {
      int n = items.size();
      for (int i = 0; i < n; i++) {
	ProjectItem item = items.get(i);
	item.writeXML(out, namespacePrefix);
      }
    }
  }

  public void loadItemsXML(Element parentElement, String namespaceURI)
    throws IOException, DocumentException {
    if (parentElement != null && parentElement.hasChildNodes()) {
      NodeList nodes = parentElement.getChildNodes();
      if (nodes != null) {
	int n = nodes.getLength();
	for (int i = 0; i < n; i++) {
	  Node node = nodes.item(i);
	  if (node.getNodeType() == Node.ELEMENT_NODE) {
	    Element e = (Element) node;
	    String uri = e.getNamespaceURI();
	    if (namespaceURI == null || namespaceURI.equals(uri)) {
	      String name = e.getLocalName();

	      if (Project.TAG_NAME_PROJECT_ITEM.equals(name)) {
		String itemName = e.getAttribute("name");
		String itemType = e.getAttribute("type");
		ProjectItem item = new ProjectItem(itemName);
		item.setItemType(Document.toDocumentType(itemType));
		try { 
		  addItem(item);
		  item.loadXML(e, namespaceURI);
		} catch (IOException ex) {
		  logError("Error loading " + itemName + "\n\t" + ex.getMessage());
		  removeItem(item);
		} catch (DocumentException ex) {
		  logError("Error loading " + itemName + "\n\t" + ex.getMessage());
		  removeItem(item);
		} 
	      } else if (Project.TAG_NAME_PROJECT_FOLDER.equals(name)) {
		String folderName = e.getAttribute("name");
		String folderType = e.getAttribute("type");
		ProjectFolder folder = new ProjectFolder(folderName, 
							 Type.toFolderType(folderType));
		addItem(folder);
		folder.loadItemsXML(e, namespaceURI);
	      } else {
		loadItemInfo(node, name);
	      }
	    }
	  }
	}
      }
    }
  }

  public void saveAllDocuments() throws IOException, DocumentException {
    int n = items.size();
    for (int i = 0; i < n; i++) {
      ProjectItem item = items.get(i);
      if (item instanceof ProjectFolder) {
	ProjectFolder folder = (ProjectFolder) item;
	folder.saveAllDocuments();
      } else {
	item.saveDocument();
      }
    }
    changed = false;
  }

  public String getNewItemName(String prefix) {
    return getNewItemName(prefix, null);
  }
  
  public String getNewItemName(String prefix, String ext) {
    if (prefix == null) {
      prefix = "NewItem";
    }
    int i = 1;
    String newItemName = (prefix + i);
    while (true) {
      if (!hasChildByName(newItemName)) {
	String filename = getFullPath() + File.separator + newItemName;
	if (ext != null) {
	  filename += ext;
	}
	File file = new File(filename);
	if (!file.exists()) { 
	  break;
	}
      }
      newItemName = (prefix + (++i));
    }
    return newItemName;
  }

  @Override
  public ProjectItem cloneProjectItem() { 
    ProjectFolder newfolder = new ProjectFolder(name); 
    int n = items.size();
    for (int i = 0; i < n; i++) {
      ProjectItem item = items.get(i);
      newfolder.addItem(item.cloneProjectItem());
    }
    return newfolder; 
  }
  
  protected Type folderType;

  protected List<ProjectItem> items = new ArrayList<ProjectItem>();

  protected boolean changed = false;

}
